package com.xuzmian.demo.kotlin

/**
 * 内联类仅在 Kotlin 1.3 之后版本可用，目前还是实验性的。
 * 有时候，业务逻辑需要围绕某种类型创建包装器。然而，由于额外的堆内存分配问题，它会引入运行时的性能开销。
 * 此外，如果被包装的类型是原生类型，性能的损失是很糟糕的，因为原生类型通常在运行时就进行了大量优化，然而
 * 他们的包装器却没有得到任何特殊的处理。为了解决这类问题，Kotlin 引入了一种被称为 内联类 的特殊类，它通
 * 过在类的前面定义一个 inline 修饰符来声明：
 */
inline class Password(val value: String)

/**
 * 内联类必须含有唯一的一个属性在主构造函数中初始化。在运行时，将使用这个唯一属性来表示内联类的实例）：
 * 不存在 'Password' 类的真实实例对象，在运行时，'securePassword' 仅仅包含 'String'
 */
val securePassword = Password("Don't try this in production")

/**
 * 内联类不能含有 init 代码块
 * 内联类不能含有幕后字段，因此，内联类只能含有简单的计算属性（不能含有延迟初始化/委托属性）
 */
inline class Name(val s: String) {
    val length: Int
        get() = s.length

    fun greet() {
        println("Hello, $s")
    }
}

fun inlineTest() {
    val name = Name("Kotlin")
    name.greet() // `greet` 方法会作为一个静态方法被调用
    println(name.length) // 属性的 get 方法会作为一个静态方法被调用
}


/**
 * 内联类允许去继承接口，但禁止内联类参与到类的继承关系结构中。这就意味着内联类不能继承其他的类而且必须是 final。
 */
interface Printable {
    fun prettyPrint(): String
}

inline class PrintableImpl(val s: String) : Printable {
    override fun prettyPrint(): String = "Let's $s!"
}

fun inlineImplementInterfaceTest() {
    val name = PrintableImpl("Kotlin")
    println(name.prettyPrint()) // 仍然会作为一个静态方法被调用
}

/**
 * 在生成的代码中，Kotlin 编译器为每个内联类保留一个包装器。内联类的实例可以在运行时表示为包装器或者基础类型。
 * 这就类似于 Int 可以表示为原生类型 int 或者包装器 Integer。
 * 为了生成性能最优的代码，Kotlin 编译更倾向于使用基础类型而不是包装器。 然而，有时候使用包装器是必要的。一般来说，
 * 只要将内联类用作另一种类型，它们就会被装箱。
 *
 * 内联类与类型别名区别：内联类引入了一个真实的新类型，与类型别名正好相反，类型别名仅仅是为现有的类型取了个新的替代名称(别名)：
 * */
interface I

inline class Foo(val i: Int) : I
fun asInline(f: Foo) {}
fun <T> asGeneric(x: T) {}
fun asInterface(i: I) {}
fun asNullable(i: Foo?) {}
fun <T> id(x: T): T = x

fun inlineBox() {
    val f = Foo(42)

    asInline(f)    // 拆箱操作: 用作 Foo 本身
    asGeneric(f)   // 装箱操作: 用作泛型类型 T
    asInterface(f) // 装箱操作: 用作类型 I
    asNullable(f)  // 装箱操作: 用作不同于 Foo 的可空类型 Foo?

    // 在下面这里例子中，'f' 首先会被装箱（当它作为参数传递给 'id' 函数时）然后又被拆箱（当它从'id'函数中被返回时）
    // 最后， 'c' 中就包含了被拆箱后的内部表达(也就是 '42')， 和 'f' 一样
    val c = id(f)
}